#include "at32f4xx.h"
#include "delay.h" 

static volatile int sysTickPending = 0;
static volatile uint32_t systick_cnt_ms = 0;
static volatile int32_t systick_calibration_ms = 0;
static volatile int32_t systick_calibration_us = 0;
// cycles per microsecond
static uint32_t usTicks = 0;
void set_millis_calibration(int32_t calibration)
{
	systick_calibration_ms = calibration;
}
void set_micros_calibration(int32_t calibration)
{
	systick_calibration_us = calibration;
}
void delay_us(uint16_t us)
{
	uint32_t now = micros();
	while (micros() - now < us);
}
void delay_ms(uint32_t ms)
{
	while(ms--)
		delay_us(1000);
}
void SysTick_Init(uint32_t div)//set number of ticks between two interruption
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//150MHz
	SysTick_Config(div);
	RCC_ClockType clocks;
	RCC_GetClocksFreq(&clocks);
	usTicks = clocks.SYSCLK_Freq / 1000000;
}
uint32_t microsISR(void)
{
	register uint32_t ms, pending, cycle_cnt;

	ATOMIC_BLOCK(NVIC_PRIO_MAX) {
		cycle_cnt = SysTick->VAL;

		if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) {
			// Update pending.
			// Record it for multiple calls within the same rollover period
			// (Will be cleared when serviced).
			// Note that multiple rollovers are not considered.

			sysTickPending = 1;

			// Read VAL again to ensure the value is read after the rollover.

			cycle_cnt = SysTick->VAL;
		}

		ms = systick_cnt_ms;
		pending = sysTickPending;
	}

	return (((ms + pending) * 1000) + (usTicks * 1000 - cycle_cnt) / usTicks + systick_calibration_us);
}
/*system microsecond counter*/
uint32_t micros(void)
{
	register uint32_t ms, cycle_cnt;

	// Call microsISR() in interrupt and elevated (non-zero) BASEPRI context

	if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) || (__get_BASEPRI())) {
		return microsISR();
	}

	do {
		ms = systick_cnt_ms;
		cycle_cnt = SysTick->VAL;
		/*
		 * If the SysTick timer expired during the previous instruction, we need to give it a little time for that
		 * interrupt to be delivered before we can recheck sysTickUptime:
		 */
		asm volatile("\tnop\n");
	} while (ms != systick_cnt_ms);

	return ((ms * 1000) + (usTicks * 1000 - cycle_cnt) / usTicks + systick_calibration_us);
}
/*system millisecond counter*/
time_ms millis(void)
{
	// if(systick_cnt_ms > systick_calibration_ms)
	return (systick_cnt_ms + systick_calibration_ms);
	// else return 0;
}
/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
	ATOMIC_BLOCK(NVIC_PRIO_MAX) {
		systick_cnt_ms++;
		sysTickPending = 0;
		(void)(SysTick->CTRL);
	}
	//if (systick_us > 0) systick_us--;

	//systick_cnt++;
	//if (systick_cnt > 999)
	//{
	//	
	//	systick_cnt = 0;
	//}
}